﻿using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;

namespace Elderos.TextSearchers
{
	public class RabinKarpSearcher : TextSearcherBase
	{
		public RabinKarpSearcher(int prefixLength = 2)
		{
			_prefixLength = prefixLength;
		}

		private readonly int _prefixLength;

        private HashSet<string> _synonyms = new HashSet<string>(StringComparer.InvariantCultureIgnoreCase);  

		private Dictionary<string, List<int>> _keywords =
            new Dictionary<string, List<int>>(StringComparer.InvariantCultureIgnoreCase);

		#region Implementation of ITextSearcher

		private int _loadedStringsCount;

		public override int LoadedStringsCount
		{
			get { return _loadedStringsCount; }
		}

		protected override IEnumerable<Position> InnerSearch(string text)
		{
			var searchResults = new List<Position>();

			for (int i = 0; i < text.Length; i++)
			{
				for (int k = 1; k < _prefixLength && i + k <= text.Length; k++)
				{
					string pretender = text.Substring(i, k);
					if (_synonyms.Contains(pretender))
						searchResults.Add(new Position(i, k));
				}

				if (i + _prefixLength > text.Length) continue;

				string currentPrefix = text.Substring(i, _prefixLength);

			    List<int> lengths;
                if (!_keywords.TryGetValue(currentPrefix, out lengths))
					continue;
                foreach (int stringLength in lengths)
				{
					if (i + stringLength > text.Length) continue;

					string pretender = text.Substring(i, stringLength);
					if (_synonyms.Contains(pretender))
						searchResults.Add(new Position(i, stringLength));
				}
			}
			return searchResults;
		}

		#endregion

		protected override void ProcessString(string str)
		{
			_loadedStringsCount++;

		    _synonyms.Add(str);

		    if (str.Length < _prefixLength) return;
            
		    string prefix = str.Substring(0, _prefixLength);
			if (!_keywords.ContainsKey(prefix))
				_keywords.Add(prefix, new List<int>());
		    var lengths = _keywords[prefix];
            if (!lengths.Contains(str.Length))
                lengths.Add(str.Length);
		}

		protected override void OnStringsLoaded()
		{
			base.OnStringsLoaded();
			foreach (var value in _keywords.Values)
				value.TrimExcess();
		}
	}
}
